import os
import warnings
import tkinter as tk
from tkinter import ttk, messagebox
from concurrent.futures import ProcessPoolExecutor, as_completed
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib import rcParams
from technical import cal_macd, cal_rsi, cal_kdj
from function import load_file, merge_index

# 中文显示配置
plt.rcParams['font.sans-serif'] = ['SimHei']  # Windows系统中文支持
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
warnings.filterwarnings("ignore")
pd.set_option('expand_frame_repr', False)
pd.set_option('display.max_rows', 5000)


class StockAnalysisApp:
    def __init__(self, root):
        self.root = root
        self.root.title("股票策略分析系统")
        self.root.geometry("1400x800")

        # 颜色配置
        self.UP_COLOR = '#FF6B6B'  # 鲜艳红色
        self.DOWN_COLOR = '#4ECDC4'  # 清新绿色

        # 初始化参数
        self.strategy_var = tk.StringVar()
        self.current_figure = None
        self._init_parameters()
        self._create_widgets()

    def _init_parameters(self):
        self.params = {
            'c_rate': 1.5 / 10000,
            't_rate': 1 / 1000,
            'day_list': [1, 2, 3, 4, 5, 10, 20],
            'strategies': {
                'MACD': 'cal_macd',
                'RSI': 'cal_rsi',
                'KDJ': 'cal_kdj'
            },
            'file_path': r'股票历史日线数据/'
        }

    def _create_widgets(self):
        # 控制面板
        control_frame = ttk.Frame(self.root)
        control_frame.pack(fill=tk.X, padx=10, pady=5)

        # 策略选择
        ttk.Label(control_frame, text="分析策略:").grid(row=0, column=0, padx=5)
        self.strategy_combo = ttk.Combobox(
            control_frame,
            textvariable=self.strategy_var,
            values=list(self.params['strategies'].keys()),
            state="readonly",
            width=15
        )
        self.strategy_combo.current(0)
        self.strategy_combo.grid(row=0, column=1, padx=5)

        # 时间范围
        ttk.Label(control_frame, text="开始日期:").grid(row=0, column=2, padx=5)
        self.start_entry = ttk.Entry(control_frame, width=10)
        self.start_entry.insert(0, '20070101')
        self.start_entry.grid(row=0, column=3, padx=5)

        ttk.Label(control_frame, text="结束日期:").grid(row=0, column=4, padx=5)
        self.end_entry = ttk.Entry(control_frame, width=10)
        self.end_entry.insert(0, '20241231')
        self.end_entry.grid(row=0, column=5, padx=5)

        # 分析按钮
        self.analyze_btn = ttk.Button(
            control_frame,
            text="开始分析",
            command=self._start_analysis
        )
        self.analyze_btn.grid(row=0, column=6, padx=10)

        # 图形显示区域（使用PanedWindow实现可调节分割）
        self.paned_window = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL)
        self.paned_window.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

        # 左侧文本结果区
        self.text_frame = ttk.Frame(self.paned_window, width=400)
        self.text_area = tk.Text(self.text_frame, wrap=tk.WORD, font=('Microsoft YaHei', 10))
        text_scroll = ttk.Scrollbar(self.text_frame, command=self.text_area.yview)
        self.text_area.configure(yscrollcommand=text_scroll.set)
        self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        text_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.paned_window.add(self.text_frame)

        # 右侧图表区
        self.chart_frame = ttk.Frame(self.paned_window)
        self.figure = plt.Figure(figsize=(10, 6), dpi=100)
        self.canvas = FigureCanvasTkAgg(self.figure, master=self.chart_frame)
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        self.paned_window.add(self.chart_frame)

        # 状态栏
        self.status_var = tk.StringVar()
        status_bar = ttk.Label(
            self.root,
            textvariable=self.status_var,
            relief=tk.SUNKEN
        )
        status_bar.pack(side=tk.BOTTOM, fill=tk.X)

        # 进度条
        self.progress = ttk.Progressbar(
            self.root,
            orient=tk.HORIZONTAL,
            mode='determinate'
        )
        self.progress.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=5)

    def _start_analysis(self):
        try:
            self._clear_display()
            selected_strategy = self.strategy_var.get()
            self._update_status(f"正在执行 {selected_strategy} 策略分析...")

            # 准备参数
            params = {
                'start_time': self.start_entry.get(),
                'end_time': self.end_entry.get(),
                'strategy_name': self.params['strategies'][selected_strategy],
                'file_path': self.params['file_path'],
                'c_rate': self.params['c_rate'],
                't_rate': self.params['t_rate'],
                'day_list': self.params['day_list']
            }

            # 多进程处理
            with ProcessPoolExecutor() as executor:
                file_list = [f for f in os.listdir(params['file_path']) if f.endswith('.csv')]
                futures = {executor.submit(process_file, f, params): f for f in file_list}

                results = []
                total = len(futures)
                for i, future in enumerate(as_completed(futures), 1):
                    try:
                        result = future.result()
                        if result is not None:
                            results.append(result)
                    except Exception as e:
                        f = futures[future]
                        self._update_status(f"处理文件 {f} 出错: {str(e)}")
                    finally:
                        self.progress['value'] = (i / total) * 100
                        self.root.update()

            # 显示结果
            if results:
                all_df = pd.concat(results, ignore_index=True)
                self._display_text_results(all_df, selected_strategy)
                self._visualize_results(all_df, selected_strategy)
                self._update_status(f"{selected_strategy} 策略分析完成")
            else:
                messagebox.showwarning("警告", "没有生成有效分析数据")

        except Exception as e:
            messagebox.showerror("错误", f"分析失败: {str(e)}")
        finally:
            self.progress['value'] = 0

    def _clear_display(self):
        """清空所有显示内容"""
        self.text_area.delete(1.0, tk.END)
        self.figure.clf()
        self.canvas.draw()

    def _display_text_results(self, df, strategy_name):
        """在文本区域显示详细分析结果"""
        self.text_area.insert(tk.END, f"========== {strategy_name}策略分析结果 ==========\n\n", 'title')
        self.text_area.tag_configure('title', font=('Microsoft YaHei', 12, 'bold'), foreground='black',
                                     justify='center')

        for day in self.params['day_list']:
            col = f'{day}日后涨跌幅'
            up_prob = df[df[col] > 0].shape[0] / df.shape[0]
            avg_return = df[col].mean()

            # 计算上涨/下跌时的平均收益率
            up_mean = df[df[col] > 0][col].mean()
            down_mean = df[df[col] <= 0][col].mean()

            self.text_area.insert(tk.END, f"{day}天周期\n", 'subtitle')
            self.text_area.tag_configure('subtitle', font=('Microsoft YaHei', 11, 'bold'))

            self.text_area.insert(tk.END, "▸ 上涨概率: ")
            self.text_area.insert(tk.END, f"{up_prob:.2%}\n", 'up_value')

            self.text_area.insert(tk.END, "▸ 平均收益率: ")
            self.text_area.insert(tk.END, f"{avg_return:.2%} | ", 'neutral')

            self.text_area.insert(tk.END, "上涨均收: ")
            self.text_area.insert(tk.END, f"{up_mean:.2%} | ", 'up_value')

            self.text_area.insert(tk.END, "下跌均损: ")
            self.text_area.insert(tk.END, f"{down_mean:.2%}\n\n", 'down_value')

            # 设置颜色标签
            self.text_area.tag_configure('up_value', foreground=self.UP_COLOR)
            self.text_area.tag_configure('down_value', foreground=self.DOWN_COLOR)
            self.text_area.tag_configure('neutral', foreground='black')

    def _visualize_results(self, df, strategy_name):
        """绘制双图表可视化结果"""
        self.figure.clear()

        # 准备数据
        results = []
        for day in self.params['day_list']:
            col = f'{day}日后涨跌幅'
            up_prob = df[df[col] > 0].shape[0] / df.shape[0]
            avg_return = df[col].mean()
            results.append((day, up_prob, 1 - up_prob, avg_return))

        days = [x[0] for x in results]
        x_pos = range(len(days))  # 等距坐标

        # 创建双图表布局
        gs = self.figure.add_gridspec(2, 1, height_ratios=[1, 1], hspace=0.4)

        # 图表1：概率分布
        ax1 = self.figure.add_subplot(gs[0])
        up_bars = ax1.bar(x_pos, [x[1] for x in results], color=self.UP_COLOR, width=0.6, label='上涨概率')
        down_bars = ax1.bar(x_pos, [x[2] for x in results], bottom=[x[1] for x in results],
                            color=self.DOWN_COLOR, width=0.6, label='下跌概率')

        # 添加数据标签
        for i, (up, down) in enumerate(zip([x[1] for x in results], [x[2] for x in results])):
            ax1.text(i, up / 2, f'{up:.1%}', ha='center', va='center', color='white', fontweight='bold')
            ax1.text(i, up + down / 2, f'{down:.1%}', ha='center', va='center', color='white', fontweight='bold')

        ax1.set_title(f'{strategy_name}策略 - 涨跌概率分布', fontsize=12, pad=10)
        ax1.set_xticks(x_pos)
        ax1.set_xticklabels([f'{d}天' for d in days])
        ax1.set_ylim(0, 1)
        ax1.legend(loc='upper right')
        ax1.grid(axis='y', linestyle=':', alpha=0.5)

        # 图表2：收益率
        ax2 = self.figure.add_subplot(gs[1])
        returns = [x[3] for x in results]
        colors = [self.UP_COLOR if r >= 0 else self.DOWN_COLOR for r in returns]
        return_bars = ax2.bar(x_pos, returns, color=colors, width=0.6)

        # 添加收益率标签
        for i, rect in enumerate(return_bars):
            height = rect.get_height()
            ax2.text(rect.get_x() + rect.get_width() / 2., height,
                     f'{height:.2%}',
                     ha='center', va='bottom' if height >= 0 else 'top',
                     color='black', fontsize=10)

        ax2.set_title(f'{strategy_name}策略 - 平均收益率', fontsize=12, pad=10)
        ax2.set_xticks(x_pos)
        ax2.set_xticklabels([f'{d}天' for d in days])
        ax2.axhline(0, color='black', linewidth=0.8)
        ax2.grid(axis='y', linestyle=':', alpha=0.5)

        self.figure.suptitle(
            f'{strategy_name}策略分析结果 ({self.start_entry.get()}至{self.end_entry.get()})',
            fontsize=14,
            y=0.98
        )
        self.figure.tight_layout()
        self.canvas.draw()

    def _update_status(self, message):
        self.status_var.set(message)
        self.root.update()


def process_file(filename, params):
    """独立处理函数（不包含任何GUI相关对象）"""
    df = load_file(params['file_path'], filename)

    # 读取指数数据（每个进程独立处理）
    index_df = pd.read_csv('sh000300.csv', encoding='gbk', parse_dates=['candle_end_time'])
    index_df.rename(columns={'candle_end_time': '交易日期'}, inplace=True)

    # 合并数据
    df = merge_index(df, index_df)
    df['交易天数'] = df.index + 1
    df = df[df['交易天数'] > 250]
    if df.empty:
        return None

    # 动态调用策略函数
    strategy_func = globals()[params['strategy_name']]
    df = strategy_func(df)

    # 计算未来表现
    for day in params['day_list']:
        df[f'{day}日后涨跌幅'] = (df['收盘价'].shift(-day) / df['开盘价'].shift(-1) - 1) * (
                    1 - params['c_rate'] - params['t_rate']) * (1 - params['c_rate'])

    # 时间过滤
    df = df[
        (df['交易日期'] >= pd.to_datetime(params['start_time'])) &
        (df['交易日期'] <= pd.to_datetime(params['end_time']))
        ]

    # 通用过滤条件
    df = df[df['成交量'] > 0]
    df = df[df['开盘涨停'].shift(-1) == False]
    df = _filter_special_stocks(df)

    return df[['交易日期', '股票代码', 'signal', *[f'{d}日后涨跌幅' for d in params['day_list']]]]


def _filter_special_stocks(df):
    """独立过滤函数"""
    df['下日_是否ST'] = df['股票名称'].str.contains('ST').shift(-1)
    df['下日_是否S'] = df['股票名称'].str.contains('S').shift(-1)
    df['下日_是否退市'] = df['股票名称'].str.contains('退').shift(-1)
    return df[
        (df['下日_是否S'] == False) &
        (df['下日_是否ST'] == False) &
        (df['下日_是否退市'] == False) &
        (~df['股票代码'].str.contains('bj')) &
        (~df['股票代码'].str.contains('sh68'))
        ]


if __name__ == '__main__':
    root = tk.Tk()
    app = StockAnalysisApp(root)
    root.mainloop()